package hotairballoon.shape.classic;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;


import graph.AxisArrangement;
import graph.AxisDirection;
import graph.AxisLocation;
import graph.GenericGraph;
import graph.PlotSet;
import graph.Point;
import graph.chart.LineChart;
import graph.chart.PointChart;
import graph.chart.point.decorator.SolidCirclePointDecorator;
import hotairballoon.shape.Shape;

public class FreeHandClassicShapeConstructor extends GenericGraph implements ComponentListener{

	private static final long serialVersionUID = 1L;
	/* Three balloon construction points */
	PRS prs = new PRS();
	/* Screen coordinates of construction points */
	PRS screenPRS = new PRS();
	double alpha = 35;
	/* Balloon shape */
	Shape balloonShape = null;
	/* A chart with balloon shape */
	LineChart lineChart = new LineChart();
	/* A chart with construction point P */
	PointChart pChart = new PointChart();
	/* A chart with construction point R */
	PointChart rChart = new PointChart();
	/* A chart with construction point S */
	PointChart sChart = new PointChart();
	/* Diameter of PRS points on the screen, in pixels */
	private int prsDiameter = 12;

	private boolean pointPDraggingAllowed = false;
	private boolean pointSDraggingAllowed = false;
	private boolean pointRDraggingAllowed = false;
	
	public FreeHandClassicShapeConstructor(){
		/* Disable the autoscale; will not be problems with scaling during moving the PRS points*/
		autoZoomToFit = false;
		
//		this.setxAxisArrangement(AxisArrangement.Horizontal);
//		this.setxAxisDirection(AxisDirection.Normal);
//		this.setxAxisLocation(AxisLocation.Bottom);
//		
//		this.setyAxisArrangement(AxisArrangement.Vertical);
//		this.setyAxisDirection(AxisDirection.Normal);
//		this.setyAxisLocation(AxisLocation.Left);
//		
		this.setxAxisArrangement(AxisArrangement.Vertical);
		this.setxAxisDirection(AxisDirection.Normal);
		this.setxAxisLocation(AxisLocation.Center);
		
		this.setyAxisArrangement(AxisArrangement.Horizontal);
		this.setyAxisDirection(AxisDirection.Normal);
		this.setyAxisLocation(AxisLocation.Bottom);
		
		/* Calculate the screen coordinates of PRS points */
		calculateScreenPRSfromPRS();
		
		ClassicShapeConstructor engine = new ClassicShapeConstructor();
		balloonShape = engine.recalculateShape(prs, 50);
		
		/* Add a chart with balloon shape */
		PlotSet plotSet = new PlotSet();
		plotSet.setPoints(balloonShape.getShapePoints());
		plotSet.setxAxisMirrorEnabled(true);
		plotSet.setPointDecorator(new SolidCirclePointDecorator(Color.BLACK, 3));
		lineChart.setPlotSet(plotSet);
		addChartToGraph(lineChart);
		
		/* Add a chart with construction point P */
		PlotSet pPlotSet = new PlotSet();
		pPlotSet.addPoint(prs.getP());
		pPlotSet.setPointDecorator(new SolidCirclePointDecorator(Color.BLUE, prsDiameter/2));
		pChart.setPlotSet(pPlotSet);
		addChartToGraph(pChart);
		
		/* Add a chart with construction point R */
		PlotSet rPlotSet = new PlotSet();
		rPlotSet.addPoint(prs.getR());
		rPlotSet.setPointDecorator(new SolidCirclePointDecorator(Color.RED, prsDiameter/2));
		rChart.setPlotSet(rPlotSet);
		addChartToGraph(rChart);
		
		/* Add a chart with construction point S */
		PlotSet sPlotSet = new PlotSet();
		sPlotSet.addPoint(prs.getS());
		sPlotSet.setPointDecorator(new SolidCirclePointDecorator(Color.BLACK, prsDiameter/2));
		sChart.setPlotSet(sPlotSet);
		addChartToGraph(sChart);
		
		/* add component listener */
		this.addComponentListener(this);
		
		this.setShowMouseScreenCoordinates(true);
		this.setShowMousePointCoordinates(true);
	}

	@Override
	protected void paintGraph(Graphics2D g2D){
		/* Paint all the standard charts on graph */
		super.paintGraph(g2D);
		/* recalculate the screen PRS from regular PRS coordinates */
		calculateScreenPRSfromPRS();
	}
	
	private boolean isMouseOverPointP(int mouseX, int mouseY){
		int distance = (int)(Math.sqrt(Math.pow(mouseX - screenPRS.getP().getX(), 2) + Math.pow(mouseY - screenPRS.getP().getY(), 2) ));
		
		if(distance <= prsDiameter/2){
		    return true;
		}
		return false;
	}
	
	private boolean isMouseOverPointR(int mouseX, int mouseY){
		int distance = (int)(Math.sqrt(Math.pow(mouseX - screenPRS.getR().getX(), 2) + Math.pow(mouseY - screenPRS.getR().getY(), 2) ));
	
		if(distance <= prsDiameter/2){
		    return true;
		}
		return false;
	}
	
	private boolean isMouseOverPointS(int mouseX, int mouseY){
		int distance = (int)(Math.sqrt(Math.pow(mouseX - screenPRS.getS().getX(), 2) + Math.pow(mouseY - screenPRS.getS().getY(), 2) ));

		if(distance <= prsDiameter/2){
		    return true;
		}
		return false;
	}

	public void calculateScreenPRSfromPRS(){
		screenPRS.setP(pointToScreenCoordinates(prs.getP()));
		screenPRS.setR(pointToScreenCoordinates(prs.getR()));
		screenPRS.setS(pointToScreenCoordinates(prs.getS()));
	}
	
	private void setScreenP(int Px, int Py){
		Point point = screenToPointCoordinates(new Point(Px, Py));
		setP(point.getX(), point.getY());
		
		calculateScreenPRSfromPRS();
	}

	private void setScreenR(int Rx, int Ry){
		Point point = screenToPointCoordinates(new Point(Rx, Ry));
		setR(point.getX(), point.getY());

		calculateScreenPRSfromPRS();
	}
	
	private void setScreenS(int Sx, int Sy){
		Point point = screenToPointCoordinates(new Point(Sx, Sy));
		setS(point.getX(), point.getY());
		
		calculateScreenPRSfromPRS();
	}

	
	
	/***
	* Sets/calculates the R point coordinates. Recalculate the R, S points coordinates if needed
	* @param Px
	* @param Py
	*/
	private void setP(double Px, double Py) {
		
		boolean alphaLocked = false;
		
		if(alphaLocked){
		    prs.getP().setX(Px);
		    prs.getP().setY(prs.getP().getX() * Math.tan(Math.toRadians(alpha)));
		}
		else{
		    prs.getP().setX(Px);
		    prs.getP().setY(Py);
		}
	
		prs.getS().setY(0);
		this.prs.getS().setX(prs.getP().getX() + Math.pow(prs.getP().getY(), 2)/Px);
	
		/* By moving point P we need to remember of point R */
		/* The equation should always be fulfilled: Rx <= Px */
		if(prs.getR().getX() > prs.getP().getX()){
		    prs.getR().setX(prs.getP().getX());
		} 
		recalculate();
	}
	
	/***
	* Sets/calculates the R point coordinates
	* @param Rx
	* @param Ry
	*/
	private void setR(double Rx, double Ry) {
		// Tylko jesli x miesci sie w odpowiednim przedziale 
		if( (Rx <= prs.getP().getX()) && (Rx >= 0)){
		    prs.getR().setX(Rx);
		    recalculate();
		}
	}
	
	/***
	* Sets/calculates the S point coordinates
	* @param Sx
	* @param Sy
	*/
	private void setS(double Sx, double Sy) {
		/* Only if Sx is between some proper values */ 
		if( (Sx <= prs.getP().getX() + Math.pow(prs.getP().getY(), 2)/prs.getP().getX()) && (Sx >= prs.getP().getX())){
		    prs.getS().setX(Sx);
		    prs.getS().setY(-(prs.getP().getX()/prs.getP().getY())*(Sx-prs.getP().getX()) + prs.getP().getY());
		}
	
		recalculate();
	}
	
	/***
	* Recalculate the new balloon shape based on the PRS points
	*/
	private void recalculate(){
		ClassicShapeConstructor engine = new ClassicShapeConstructor();
		balloonShape = engine.recalculateShape(prs, 50);
		
		lineChart.getPlotSet().setPoints(balloonShape.getShapePoints());
		
		repaint();
	}
	
	@Override
	public void componentHidden(ComponentEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void componentMoved(ComponentEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void componentResized(ComponentEvent arg0) {
		// TODO Auto-generated method stub
		zoomToFit();
	}

	@Override
	public void componentShown(ComponentEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void mouseReleased(MouseEvent me) {     
		int button = me.getButton();

		if(button == MouseEvent.BUTTON1){
		    pointPDraggingAllowed = false;
		    pointSDraggingAllowed = false;
		    pointRDraggingAllowed = false;
		}
	} 
	
	@Override
	public void mousePressed(MouseEvent me) {

		int mouseX = (int) me.getPoint().getX();
		int mouseY = (int) me.getPoint().getY();
		int button = me.getButton();

		if(button == MouseEvent.BUTTON1){
			/* Left mouse button has been pressed */
		    if(isMouseOverPointP(mouseX, mouseY)){
		        pointPDraggingAllowed = true;
		    }
		    else
		        if(isMouseOverPointS(mouseX, mouseY)){
		            pointSDraggingAllowed = true;
		        }
		        else
		            if(isMouseOverPointR(mouseX, mouseY)){
		            pointRDraggingAllowed = true;
		        }
		}
	}
	
	public void mouseDragged(MouseEvent me) {
		super.mouseDragged(me);
		
		int mouseX = (int) me.getPoint().getX();
		int mouseY = (int) me.getPoint().getY();
		
		if(this.pointPDraggingAllowed){
		    setScreenP(mouseX,mouseY);
		}
		else
		    if(this.pointSDraggingAllowed){
		        setScreenS(mouseX,mouseY);
		    }
		    else
		        if(this.pointRDraggingAllowed){
		            setScreenR(mouseX,mouseY);
		        }

		repaint();
	}
}
